using DotNetCommon.Extensions;
using DotNetCommon.Logger;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace DotNetCommon.Expressions
{
    internal class ExpressionNode
    {
        private readonly ILogger<ExpressionNode> logger = LoggerFactory.CreateLogger<ExpressionNode>();
        /// <summary>
        /// 当前节点的表达式
        /// </summary>
        internal Expression Expression { get; set; }
        /// <summary>
        /// 表达式类型
        /// </summary>
        internal ExpressionType NodeType => Expression.NodeType;
        /// <summary>
        /// 子节点
        /// </summary>
        internal List<ExpressionNode> Children { get; set; }
        /// <summary>
        /// 父节点
        /// </summary>
        internal ExpressionNode Parent { get; set; }

        /// <summary>
        /// 当期节点有更新
        /// </summary>
        internal bool _updateRequest { get; set; }

        /// <summary>
        /// 当前节点是否有更新
        /// </summary>
        internal bool NeedUpdate => Children.IsNullOrEmpty() ? _updateRequest : Children.Any(i => i.NeedUpdate);


        internal List<ParameterExpression> _parameters;
        /// <summary>
        /// 源参数列表
        /// </summary>
        internal List<ParameterExpression> Parameters => Parent == null ? _parameters : Parent.Parameters;
        /// <summary>
        /// 当前节点的完整唯一描述(基于此实现缓存)
        /// </summary>
        internal string FullMarkString { get; set; }
        /// <summary>
        /// 当前节点是否是源参数
        /// </summary>
        internal bool IsParameter { get; set; }
        /// <summary>
        /// 当前节点是否含源参数
        /// </summary>
        internal bool HasParameter => IsParameter ? true : Children.IsNotNullOrEmpty() && Children.Any(i => i.HasParameter);
        /// <summary>
        /// 当前节点是否是局部变量
        /// </summary>
        internal bool IsLocalVariable { get; set; }

        private static ConcurrentDictionary<string, Delegate> _caches = new ConcurrentDictionary<string, Delegate>();


        /// <summary>
        /// 实际的简化方法: 以FullMarkString为key从缓存中取到缓存好的委托(无参函数),直接调用即可
        /// </summary>
        internal object Reduce()
        {
            //使用FullMarkString对于那些复杂 Object 是否能区别开? 比如: 常量 dic:{"name","小明"} 和 dic: {"age":18}
            //首先,这种非基础类型的常量不可能直接出现在表达式中,只可能是由子节点简化而来,而由简化得来的节点是不可能再次参与简化的
            var dele = _caches.GetOrAdd(FullMarkString, key =>
            {
                var lambda = LambdaExpression.Lambda(Expression, new ParameterExpression[0]);
                var dele = lambda.Compile();
                logger.LogDebug($"Reduce: lambda.Compile,FullMarkString={FullMarkString}");
                return dele;
            });
            return dele.DynamicInvoke(new object[0]);
        }
    }
}
